﻿namespace Blog.Tests
{
    using System;
    using System.Net;
    using System.Net.Http;
    using System.Transactions;
    using Blog.WebAPI.Controllers;
    using Blog.WebAPI.Models;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Newtonsoft.Json;

    [TestClass]
    public class UsersControllerIntegrationTests
    {
        private static UserRegisterModel validUserModel = new UserRegisterModel()
        {
            Username = "ValidUsername",
            DisplayName = "Valid Display Name",
            AuthCode = new string('x', 40)
        };

        private static TransactionScope scope;

        [TestInitialize]
        public void TestInitialize()
        {
            scope = new TransactionScope();
        }

        [TestCleanup]
        public void TestCleanup()
        {
            scope.Dispose();
        }

        [TestMethod]
        public void Register_WhenUserValid_ShouldSaveToDatabaseAndReturnCreated()
        {
            var response = this.LoginUser(validUserModel);

            Assert.AreEqual(HttpStatusCode.Created, response.StatusCode);
            Assert.IsNotNull(response.Content);

            var content = response.Content.ReadAsStringAsync().Result;
            var model = JsonConvert.DeserializeObject<UserLoggedModel>(content);

            Assert.AreEqual(validUserModel.DisplayName, model.DisplayName);
            Assert.IsNotNull(model.SessionKey);
        }

        [TestMethod]
        public void Register_WhenUsernameIsNull_ShouldReturnBadRequest()
        {
            var user = new UserRegisterModel()
            {
                DisplayName = "Valid Display Name",
                AuthCode = new string('x', 40)
            };

            var response = this.LoginUser(user);

            Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
        }

        [TestMethod]
        public void Register_WhenUsernameTooShort_ShouldReturnBadRequest()
        {
            var user = new UserRegisterModel()
            {
                Username = "Short",
                DisplayName = "Valid Display Name",
                AuthCode = new string('x', 40)
            };

            var response = this.LoginUser(user);

            Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
        }

        [TestMethod]
        public void Register_WhenUsernameTooLong_ShouldReturnBadRequest()
        {
            var user = new UserRegisterModel()
            {
                Username = "Loremipsumdolorsitametconsecteturadipiscingelit",
                DisplayName = "Valid Display Name",
                AuthCode = new string('x', 40)
            };

            var response = this.LoginUser(user);

            Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
        }

        [TestMethod]
        public void Register_WhenUsernameContainsInvalidChars_ShouldReturnBadRequest()
        {
            var user = new UserRegisterModel()
            {
                Username = "Invalid@Username",
                DisplayName = "Valid Display Name",
                AuthCode = new string('x', 40)
            };

            var response = this.LoginUser(user);

            Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
        }

        [TestMethod]
        public void Register_WhenDisplayNameIsNull_ShouldReturnBadRequest()
        {
            var user = new UserRegisterModel()
            {
                Username = "ValidUsername",
                AuthCode = new string('x', 40)
            };

            var response = this.LoginUser(user);

            Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
        }

        [TestMethod]
        public void Register_WhenDisplayNameTooShort_ShouldReturnBadRequest()
        {
            var user = new UserRegisterModel()
            {
                Username = "ValidUsername",
                DisplayName = "Short",
                AuthCode = new string('x', 40)
            };

            var response = this.LoginUser(user);

            Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
        }

        [TestMethod]
        public void Register_WhenDisplayNameTooLong_ShouldReturnBadRequest()
        {
            var user = new UserRegisterModel()
            {
                Username = "ValidUsername",
                DisplayName = "Lorem ipsum dolor sit amet, consectetur adipiscing elit",
                AuthCode = new string('x', 40)
            };

            var response = this.LoginUser(user);

            Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
        }

        [TestMethod]
        public void Register_WhenDisplayNameContainsInvalidChars_ShouldReturnBadRequest()
        {
            var user = new UserRegisterModel()
            {
                Username = "ValidUsername",
                DisplayName = "Invalid @ Display Name",
                AuthCode = new string('x', 40)
            };

            var response = this.LoginUser(user);

            Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
        }

        [TestMethod]
        public void Register_WhenAuthCodeIsNull_ShouldReturnBadRequest()
        {
            var user = new UserRegisterModel()
            {
                Username = "ValidUsername",
                DisplayName = "Valid Display Name"
            };

            var response = this.LoginUser(user);

            Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
        }

        [TestMethod]
        public void Register_WhenAuthCodeIsDifferentLength_ShouldReturnBadRequest()
        {
            var user = new UserRegisterModel()
            {
                Username = "ValidUsername",
                DisplayName = "Valid Display Name",
                AuthCode = new string('x', 20)
            };

            var response = this.LoginUser(user);

            Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
        }

        [TestMethod]
        public void Register_WhenAuthCodeContainsInvalidChars_ShouldReturnBadRequest()
        {
            var user = new UserRegisterModel()
            {
                Username = "ValidUsername",
                DisplayName = "Valid Display Name",
                AuthCode = new string('@', 40)
            };

            var response = this.LoginUser(user);

            Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
        }

        [TestMethod]
        public void Logout_WhenUsingValidSessionKey_ShouldUpdateDatabaseAndReturnOK()
        {
            var registerResponse = this.LoginUser(validUserModel);
            var content = registerResponse.Content.ReadAsStringAsync().Result;
            var model = JsonConvert.DeserializeObject<UserLoggedModel>(content);

            var httpServer = new InMemoryHttpServer("http://localhost/");
            var logoutResponse = httpServer.CreatePutRequest("api/users/logout?sessionKey=" + model.SessionKey);
            Assert.AreEqual(HttpStatusCode.OK, logoutResponse.StatusCode);
        }

        [TestMethod]
        public void Logout_WhenUsingSessionKeyWithDifferentLength_ShouldReturnBadRequest()
        {
            var httpServer = new InMemoryHttpServer("http://localhost/");
            var logoutResponse = httpServer.CreatePutRequest("api/users/logout?sessionKey=" + new string('x', 20));
            Assert.AreEqual(HttpStatusCode.BadRequest, logoutResponse.StatusCode);
        }

        [TestMethod]
        public void Logout_WhenUsingSessionKeyContainingInvalidChars_ShouldReturnBadRequest()
        {
            var httpServer = new InMemoryHttpServer("http://localhost/");
            var logoutResponse = httpServer.CreatePutRequest("api/users/logout?sessionKey=" + new string('*', 20));
            Assert.AreEqual(HttpStatusCode.BadRequest, logoutResponse.StatusCode);
        }

        private HttpResponseMessage LoginUser(UserRegisterModel user)
        {
            var httpServer = new InMemoryHttpServer("http://localhost/");
            var response = httpServer.CreatePostRequest("api/users/register", user);

            return response;
        }
    }
}
